/**
 * @file		CMDinterpreter.c
 * @brief		Command Interpreter
 * @note		None
 * @attention	None
 * 
 * <B><I>ALL RIGHTS RESERVED, COPYRIGHT&copy; SOCIONEXT INCORPORATED 2016</I></B>
 */

/* Standard includes. */
#include <stdio.h>

/* CMSIS include */
#include "ARMCM0.h"

/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "event_groups.h"
#include "task.h"

/* Base function includes */
#include "CMDinterpreter.h"
#include "statemachine.h"
#include "i2c.h"
#include "spi.h"
#include "uart.h"
#include "TemperatureSensor.h"
#include "adc.h"
#include "ipcu.h"
#include "calendar.h"
// ### DELETE_SECURE
#include "netsec.h"
#include "loader.h"
// ### DELETE_SECURE

#include "dd_top.h"
#include "exstop.h"

typedef int (*CMD_FUNC)(unsigned *);

static int prvInterpretCommandGo(unsigned *cmd);
static int prvInterpretCommandWrite(unsigned *cmd);
static int prvInterpretCommandRead(unsigned *cmd);
static int prvInterpretCommandRMW(unsigned *cmd);
static int prvInterpretCommandSet(unsigned *cmd);
static int prvInterpretCommandGet(unsigned *cmd);
static int prvInterpretCommandStart(unsigned *cmd);
static int prvInterpretCommandStop(unsigned *cmd);
// ### DELETE_SECURE
#if 1
static int prvInterpretCommandCrypto(unsigned *cmd);
#else
// ### DELETE_SECURE
static int prvInterpretCommandDummy(unsigned *cmd);
// ### DELETE_SECURE
#endif
// ### DELETE_SECURE
static int prvInterpretCommandStateAfterStandby(unsigned *cmd);


static int prvInterpretCommandGo(unsigned *cmd)
{
	if( iStateMachineSetState( STATE_SLEEP ) != 0) {
		// status error
		return D_IPCU_ERROR_TYPE_STATUS_ERROR;
	}
	
	return 0;
}

static int prvInterpretCommandWrite(unsigned *cmd)
{
	int ret = 0;
	switch( cmd[1] ) {
		case 0:
			*((unsigned *)(cmd[2])) = cmd[3];
			break;
		case 1:
			*((unsigned short *)(cmd[2])) = (unsigned short)(cmd[3]);
			break;
		case 2:
			*((unsigned char *)(cmd[2])) = (unsigned char)(cmd[3]);
			break;
		default:
			// commnand parameter error
			ret = D_IPCU_ERROR_TYPE_PARAMETER_NG;
			break;
	}
	return ret;
}

static int prvInterpretCommandRead(unsigned *cmd)
{
	int ret = 0;
	unsigned uAns = 0;
	unsigned short usAns;
	unsigned char ucAns;
	
	switch( cmd[1] ) {
		case 0:
			uAns = *((unsigned *)(cmd[2]));
			break;
		case 1:
			usAns = *((unsigned short *)(cmd[2]));
			uAns = usAns;
			break;
		case 2:
			ucAns = *((unsigned char *)(cmd[2]));
			uAns = ucAns;
			break;
		default:
			// commnand parameter error
			ret = D_IPCU_ERROR_TYPE_PARAMETER_NG;
			break;
	}
//	cmd[0] |= 0x01000000;
	cmd[8] = uAns;
	
	return ret;
}

static int prvInterpretCommandRMW(unsigned *cmd)
{
	int ret = 0;
	unsigned uAns;
	unsigned short usAns;
	unsigned char ucAns;
	unsigned uData;
	unsigned uMask;
	
	uData = cmd[3];
	uMask = cmd[4];
	switch( cmd[1] ) {
		case 0:
			uAns = *((unsigned *)(cmd[2]));
			uAns &= ~uMask;
			uAns |= uData;
			*((unsigned *)(cmd[2])) = uAns;
			break;
		case 1:
			usAns = *((unsigned short *)(cmd[2]));
			uAns = usAns;
			uAns &= ~uMask;
			uAns |= uData;
			usAns = uAns;
			*((unsigned short *)(cmd[2])) = usAns;
			break;
		case 2:
			ucAns = *((unsigned char *)(cmd[2]));
			uAns = ucAns;
			uAns &= ~uMask;
			uAns |= uData;
			ucAns = uAns;
			*((unsigned char *)(cmd[2])) = ucAns;
			break;
		default:
			// commnand parameter error
			ret = D_IPCU_ERROR_TYPE_PARAMETER_NG;
			break;
	}
	return ret;
}

typedef int (*SET_BUF_CMD_FUNC)(void*);

static const SET_BUF_CMD_FUNC gSetBufCmdTbl[] = {
	(SET_BUF_CMD_FUNC)0,								// 0x01
	(SET_BUF_CMD_FUNC)0,								// 0x02
	(SET_BUF_CMD_FUNC)iUARTSetSendBuffer,				// 0x03
	(SET_BUF_CMD_FUNC)iUARTSetReceiveBuffer,			// 0x04
	(SET_BUF_CMD_FUNC)iSPISetSendBuffer,				// 0x05
	(SET_BUF_CMD_FUNC)iSPISetReceiveBuffer,				// 0x06
	(SET_BUF_CMD_FUNC)iI2CSetSendBuffer,				// 0x07
	(SET_BUF_CMD_FUNC)iI2CSetReceiveBuffer,				// 0x08
	(SET_BUF_CMD_FUNC)iAdcSetBuffer,					// 0x09
	(SET_BUF_CMD_FUNC)iTemperatureSensorSetBuffer		// 0x0A
};

/** Buffer data */
typedef struct {
	unsigned long		address;		/**< start address of buffer.					*/
	unsigned long		size;			/**< byte size. 0:no-buffer.					*/
	unsigned long		pointer;		/**< address of write pointer.					*/
	int					overlap;		/**< 0:no-overlap, 1:cyclic, 2:same position	*/
} T_CMD_BUFFER;

typedef int (*CMD_INTRP_NOTIFICATION_FUNC)(int);

static const CMD_INTRP_NOTIFICATION_FUNC gDmdIntrpNotifyTbl[] = {
	iUARTSendNotification,				// 0x1003
	iUARTReceiveNotification,			// 0x1004
	iSPISendNotification,				// 0x1005
	iSPIReceiveNotification,			// 0x1006
	iI2CSendNotification,				// 0x1007
	iI2CReceiveNotification,			// 0x1008
	iAdcNotification,					// 0x1009
	iTemperatureSensorNotification		// 0x100A
};

static int prvInterpretCommandSet(unsigned *cmd)
{
	unsigned id;
	id = cmd[1];
	if( 1 > id ) {
		// commnand parameter error
		return D_IPCU_ERROR_TYPE_PARAMETER_NG;
	}
	if( 0x3003 < id ) {
		// commnand parameter error
		return D_IPCU_ERROR_TYPE_PARAMETER_NG;
	}
	if( 0x07 == id ) {
		T_I2C_SEND_BUFFER i2c_send_buff;
		i2c_send_buff.address		= cmd[2];
		i2c_send_buff.size			= cmd[3];
		i2c_send_buff.slave_address	= cmd[4];
		return (gSetBufCmdTbl[id-0x01])( &i2c_send_buff );
	} else if( 0x08 == id ) {
		T_I2C_RECEIVE_BUFFER i2c_receive_buff;
		i2c_receive_buff.address		= cmd[2];
		i2c_receive_buff.size			= cmd[3];
		i2c_receive_buff.pointer		= cmd[4];
		i2c_receive_buff.overlap		= cmd[5];
		i2c_receive_buff.slave_address	= cmd[6];
		return (gSetBufCmdTbl[id-0x01])( &i2c_receive_buff );
	} else if( 0x09 >= id ) {
		T_CMD_BUFFER buff;
		buff.address = cmd[2];
		buff.size = cmd[3];
		buff.pointer = cmd[4];
		buff.overlap = cmd[5];
		return (gSetBufCmdTbl[id-0x01])( &buff );
	} else if( 0x0A == id ) {
		T_TEMP_SENSOR_BUFFER temp_buff;
		temp_buff.hi_full_temp = cmd[2];
		temp_buff.hi_near_temp = cmd[3];
		temp_buff.lo_near_temp = cmd[4];
		temp_buff.lo_full_temp = cmd[5];
		return (gSetBufCmdTbl[id-0x01])( &temp_buff );
	}
	else if( 0x0B == id ) { //E_FJ_IPCU_SET_ADVFS_EN
		return vAdvfs_DvfsSetEN((unsigned char *)&cmd[2]);
	}
	else if( 0x0B1 == id ) { //E_FJ_IPCU_SET_ADVFS
		//return vAdvfsSetDate((unsigned char *)&cmd[2]); //if allow AVS to set
		return vAdvfsSetDate((unsigned char *)&cmd[3]);
	}
	else if ( 0x0B2 == id ) { //E_FJ_IPCU_SET_PLL1_FRQ_BUF
		return SetPLL1FrqBuffer(&cmd[2]);
	}
	else if( 0x1003 > id ) {
		// commnand parameter error
		return D_IPCU_ERROR_TYPE_PARAMETER_NG;
	}
	if( 0x100A >= id ) {
		return (gDmdIntrpNotifyTbl[id-0x1003])( cmd[2] );
	}
	if( 0x2001 == id ) {
		vSetWakeupCondition( cmd[2] );
		return 0;
	}
	
	if( 0x3003 == id ) {
		return iCalendarSetExtRTC( cmd );
	}
	
	if( 0x3001 != id ) {
		// commnand parameter error
		return D_IPCU_ERROR_TYPE_PARAMETER_NG;
	}
	return iCalendarSetDate( cmd );
}

static int prvInterpretCommandGet(unsigned *cmd)
{
	int ret = 0;
	unsigned id;
	id = cmd[1];
	
	if ( 0x0A == id ) {
		if (0 !=(ret = iTemperatureSensorGetTemperatureData( cmd )) ) {
			return ret;
		}
	}
	else if( 0x0B == id ) { //E_FJ_IPCU_GET_ADVFS_EN
		vAdvfs_DvfsGetEN(&cmd[2]);
	}
	else if( 0x0B1 == id ) { //E_FJ_IPCU_GET_ADVFS
		vAdvfsGetDate(&cmd[2]);
	}
	else if( 0x0B2 == id ) { //E_FJ_IPCU_GET_PLL1_FRQ_BUF
		GetPLL1FrqBuffer(&cmd[2]);
	}
	else if( 0x0B3 == id ) { //E_FJ_IPCU_GET_TEMPERA_BUF
		T_TEMP_SENSOR_BUFFER temp_buff = {0};
		iTemperatureSensorGetBuffer(&temp_buff);
		cmd[2] = temp_buff.hi_full_temp;
		cmd[3] = temp_buff.hi_near_temp;
		cmd[4] = temp_buff.lo_near_temp;
		cmd[5] = temp_buff.lo_full_temp;
	}
	else if( 0x3001 == id ) {
		vCalendarGetDate( cmd );
	}
	else if( 0xAB01 == id ) {
		unsigned char status;
		
		vGetStateBootUpSource( &status );
		cmd[2] = status;
	}
	else {
		// commnand parameter error
		return D_IPCU_ERROR_TYPE_PARAMETER_NG;
	}
	
//	cmd[0] |= 0x01000000;
	return 0;
}

typedef int (*START_CMD_FUNC)(void);

static const START_CMD_FUNC gStartCmdTbl[] = {
	iUARTSendStart,				// 0x03
	iUARTReceiveStart,			// 0x04
	iSPISendStart,				// 0x05
	iSPIReceiveStart,			// 0x06
	iI2CSendStart,				// 0x07
	iI2CReceiveStart,			// 0x08
	iAdcStart,					// 0x09
	iTemperatureSensorStart		// 0x0A
};

static int prvInterpretCommandStart(unsigned *cmd)
{
	unsigned id;
	id = cmd[1];
	
	if( 0x03 > id ) {
		// commnand parameter error
		return D_IPCU_ERROR_TYPE_PARAMETER_NG;
	}
	if( 0x0A < id ) {
		// commnand parameter error
		return D_IPCU_ERROR_TYPE_PARAMETER_NG;
	}
	return (gStartCmdTbl[id - 0x03])();
}

typedef int (*STOP_CMD_FUNC)(void);

static const STOP_CMD_FUNC gStopCmdTbl[] = {
	iUARTReceiveStop,		// 0x04
	0,
	iSPIReceiveStop,		// 0x06
	0,
	iI2CReceiveStop,		// 0x08
	iAdcStop,				// 0x09
	iTemperatureSensorStop	// 0x0A
};

static int prvInterpretCommandStop(unsigned *cmd)
{
	unsigned id;
	id = cmd[1];
	
	if( 0x04 > id ) {
		// commnand parameter error
		return D_IPCU_ERROR_TYPE_PARAMETER_NG;
	}
	if( 0x0A < id ) {
		// commnand parameter error
		return D_IPCU_ERROR_TYPE_PARAMETER_NG;
	}
	if( NULL != gStopCmdTbl[id - 0x04] ) {
		return (gStopCmdTbl[id - 0x04])();
	}
	// commnand parameter error
	return D_IPCU_ERROR_TYPE_PARAMETER_NG;
}

// ### DELETE_SECURE
#if 1
static int prvInterpretCommandCrypto(unsigned *cmd)
{
	int				ret;
	int				loop;
	unsigned long	hash[8];
	unsigned char	netsecck = 0;
	unsigned char	netrck = 0;
	unsigned long	ram_pd = 0;
	
	if ((cmd[1] == 0) || (cmd[2] == 0) || (cmd[3] == 0)) {
		// response : failure
		cmd[8] = -1;
		return 0;
	}
	
#ifdef NETSEC_DEBUG
	// Remap
	IO_GPV.REMAP.word = 0x40;
	__DMB();
#endif	// NETSEC_DEBUG
	
	// clock check
	netsecck = Dd_Top_Get_CLKSTOP2_NETSECCK();
	if (netsecck == 1) {
		// NETSECCK on
		Dd_Top_Set_CLKSTOP2_NETSECCK(0);
	}
	netrck = Dd_Top_Get_CLKSTOP2_NETRCK();
	if (netrck == 1) {
		// NETRCK on
		Dd_Top_Set_CLKSTOP2_NETRCK(0);
	}
	
	// RAM PD Control register check.
	ram_pd = IO_EXSTOP.RAM_PD.word;
	if (ram_pd & 0x20) {
		// PD release
		IO_EXSTOP.RAM_PD.word &= ~( 0x20 );
		
		/* Wait:1000ns */
		wait(8);	// 8 = 8 * 130ns = 1040ns.
	}
	
	// EXS NETSEC softreset release
	if (IO_EXSTOP.SOFTRESET.bit.NETSECRST == 0) {
		IO_EXSTOP.SOFTRESET.word |= 0x0100;
	}
	
	// hash
	ret = hash_calculate( cmd[3], (T_HASH_DATA*)cmd[2], hash );
	
	if (netsecck == 1) {
		// NETSECCK off
		Dd_Top_Set_CLKSTOP2_NETSECCK(1);
	}
	if (netrck == 1) {
		// NETRCK off
		Dd_Top_Set_CLKSTOP2_NETRCK(1);
	}
	if (ram_pd & 0x20) {
		// PD enter
		IO_EXSTOP.RAM_PD.word |= 0x20;
		
		// wait:20ns
		wait(1);	// 1 = 130ns.
	}
	
	// check result
	if (ret != 0) {
		// response : failure
		cmd[8] = -1;
		return 0;
	}
	
	// To compare the digest data and hash data.
	for (loop = 0; loop < 8; loop++) {
		printf("Crypto : hash[%d]:0x%08lx vs digest[%d]:0x%08lx (diff:%ld)\n", loop, hash[loop], loop, *((unsigned long*)(cmd[1] + (loop * 4))), hash[loop] - *((unsigned long*)(cmd[1] + (loop * 4))));
		if ( hash[loop] != *((unsigned long*)(cmd[1] + (loop * 4))) ) {
			// response : failure
			cmd[8] = -2;
			return 0;
		}
	}
	
	// response : success(0)
	cmd[8] = 0;
	return 0;
}
#else
// ### DELETE_SECURE
static int prvInterpretCommandDummy(unsigned *cmd)
{
	// unused
	return D_IPCU_ERROR_TYPE_PARAMETER_NG;
}
// ### DELETE_SECURE
#endif
// ### DELETE_SECURE

static int prvInterpretCommandStateAfterStandby(unsigned *cmd)
{
	if( iSetStateAfterStandby( (E_STATE_AFTER_STANDY)cmd[1], cmd[2], cmd[3] ) != 0 ) {
		// commnand parameter error
		return D_IPCU_ERROR_TYPE_PARAMETER_NG;
	}
	return 0;
}

typedef struct {
	CMD_FUNC cmd_func;
} T_CMD_INTERPRETER;

static const T_CMD_INTERPRETER gCMDtbl[] = {
	{prvInterpretCommandGo},				// 0x01
	{prvInterpretCommandWrite},				// 0x02
	{prvInterpretCommandRead},				// 0x03
	{prvInterpretCommandRMW},				// 0x04
	{prvInterpretCommandSet},				// 0x05. number of parametes depend on ID.
	{prvInterpretCommandGet},				// 0x06
	{prvInterpretCommandStart},				// 0x07
	{prvInterpretCommandStop},				// 0x08
// ### DELETE_SECURE
#if 1
	{prvInterpretCommandCrypto}, 		// 0x09
#else
// ### DELETE_SECURE
	{prvInterpretCommandDummy},				// 0x09 unused.
// ### DELETE_SECURE
#endif
// ### DELETE_SECURE
	{prvInterpretCommandStateAfterStandby}	// 0x0A
};

int iInterpretCommand(unsigned *cmd)
{
	unsigned code;
	
	code = *cmd;
	if( 0x01 > code ) {
		// commnand parameter error
		return D_IPCU_ERROR_TYPE_PARAMETER_NG;
	}
	if( 0x0A < code ) {
		// commnand parameter error
		return D_IPCU_ERROR_TYPE_PARAMETER_NG;
	}
	printf("iInterpretCommand : 0x%x\n", code);
	return (gCMDtbl[code-0x01].cmd_func)(cmd);
}

EventGroupHandle_t xNotifyEventFlag;

void vNotifyTask( void *pvParameters )
{
	EventBits_t uxBits;
	
	printf("vNotifyTask() : Notify Task start.\n");

	xNotifyEventFlag = xEventGroupCreate();
	if( NULL == xNotifyEventFlag ) {
		// error
		for(;;) ;
	}
	
	for(;;) {
		uxBits = xEventGroupWaitBits(
			xNotifyEventFlag, /* The event group being tested. */
			0xFFFF,        /* The bits within the event group to wait for. */
			pdTRUE,        /* BIT  should be cleared before returning. */
			pdFALSE,       /* Don't wait for both bits, either bit will do. */
			portMAX_DELAY );/* Wait . */

//		printf("event : 0x%x\n", (unsigned short)uxBits);
		if( uxBits & (1<<NFY_UART_SEND) ) {
			vIPCUSendNotification( 0x03 );
		}
		if( uxBits & (1<<NFY_UART_RECEIVE) ) {
			vIPCUSendNotification( 0x04 );
		}
		if( uxBits & (1<<NFY_SPI_SEND) ) {
			vIPCUSendNotification( 0x05 );
		}
		if( uxBits & (1<<NFY_SPI_RECEIVE) ) {
			vIPCUSendNotification( 0x06 );
		}
		if( uxBits & (1<<NFY_I2C_SEND) ) {
			vIPCUSendNotification( 0x07 );
		}
		if( uxBits & (1<<NFY_I2C_RECEIVE) ) {
			vIPCUSendNotification( 0x08 );
		}
		if( uxBits & (1<<NFY_ADC) ) {
			vIPCUSendNotification( 0x09 );
		}
		if( uxBits & (1<<NFY_TEMPERATURE) ) {
			vIPCUSendNotification( 0x0A );
		}
		if( uxBits & (1<<NFY_SEND_RESPONSE) ) {
			vIPCUSendResponse();
		}
	}
}

